# -*- coding: utf-8 -*-
# MailArchive plugin

from datetime import datetime
import urllib
import time
import calendar
import re
import os
import tempfile
import email.Errors
import email.Utils
import mailbox
import mimetypes
import email
#from email.Parser  import Parser
from email.Header import decode_header
#from email.Utils import collapse_rfc2231_value

#011
import pkg_resources

from genshi.builder import tag
from genshi.filters.transform import Transformer


from trac.core import *
from trac.env import IEnvironmentSetupParticipant
#from trac.Search import ISearchSource, search_to_sql, shorten_result
from trac.search import ISearchSource, search_to_sql, shorten_result

from trac.web.api import IRequestHandler, IRequestFilter, ITemplateStreamFilter
from trac.util import NaivePopen
from StringIO import StringIO

from trac.ticket import Ticket


from trac.wiki import wiki_to_html,wiki_to_oneliner, IWikiSyntaxProvider
from trac.util.html import html, Markup #0.10
from trac.web.chrome import add_link, add_stylesheet,add_ctxtnav, prevnext_nav, \
                        INavigationContributor, ITemplateProvider

#0.11 from trac.attachment import attachment_to_hdf, attachments_to_hdf, Attachment, AttachmentModule
from trac.attachment import AttachmentModule


#011 from trac.Timeline import ITimelineEventProvider #same
from trac.timeline.api import ITimelineEventProvider

#011
from trac.util.translation import _
from trac.resource import *
from trac.mimeview.api import Context

from trac.mimeview import *
#from trac.mimeview.api import Mimeview, IContentConverter #0.10

#011
#from trac.perm import PermissionError, PermissionSystem, IPermissionPolicy
from trac.perm import IPermissionRequestor
from trac.resource import IResourceManager
from trac.attachment import ILegacyAttachmentPolicyDelegate

#011
from trac.util.datefmt import to_timestamp, utc

from trac.util.presentation import Paginator

from util import linkify_ids

def get_author(fromname,fromaddr):
    author = fromname
    if fromname=='':
        if re.match('(.+?)@',fromaddr):
            author = re.match('(.+?)@',fromaddr).group(1)
    if author == None or author.strip() =='':
        author = '--'
    return author

class Timeline(Component):
    implements(ITimelineEventProvider)

    # ITimelineEventProvider methods

    def get_timeline_filters(self, req):
        if 'MAILARCHIVE_VIEW' in req.perm:
            yield ('mailarchive', _(self.env.config.get('mailarchive', 'title','MailArchive')))

    def get_timeline_events(self, req, start, stop, filters):
        if 'mailarchive' in filters:
            add_stylesheet(req, 'mailarchive/css/mailarchive.css')

            db = self.env.get_db_cnx()
            mailarchive_realm = Resource('mailarchive')
            cursor = db.cursor()

            cursor.execute("SELECT id,category as mlname,utcdate as localdate,"
                           "fromname,fromaddr , subject  FROM mailarc "
                           "WHERE utcdate>=%s AND utcdate<=%s ",
                           (to_timestamp(start), to_timestamp(stop)))
            for id,category,localdate, fromname, fromaddr,subject in cursor:
                #if 'WIKI_VIEW' not in req.perm('wiki', name):
                #    continue
                author = get_author(fromname,fromaddr)
                #ctx = context('mailarchive', id)
                
                resource = mailarchive_realm(id=id,version=None)
                if 'MAILARCHIVE_VIEW' not in req.perm(resource):
                    continue
                yield ('mailarchive',
                       datetime.fromtimestamp(localdate, utc),
                       author or '--',
                       (resource,(category,author,subject)))


    def render_timeline_event(self, context, field, event):
        mailarchive_page,(category,author,subject) = event[3]
        if field == 'url':
            return context.href.mailarchive(mailarchive_page.id, version=mailarchive_page.version)
        elif field == 'title':
            markup = tag(u'メールが ',category,u'に送信されました')
            return markup
        elif field == 'description':
            markup = tag(subject)
            return markup

    
class SearchProvider(Component):
    implements(ISearchSource)

    # ISearchProvider methods

    def get_search_filters(self, req):
        if 'MAILARCHIVE_VIEW' in req.perm:
            yield ('mailarchive', self.env.config.get('mailarchive', 'title','MailArchive'))


    def get_search_results(self, req, terms, filters):
        if 'mailarchive' in filters:
            db = self.env.get_db_cnx()
            sql_query, args = search_to_sql(db,  ['m1.messageid','m1.subject','m1.fromname','m1.fromaddr','m1.text'],terms)
            cursor = db.cursor()
            cursor.execute("SELECT m1.id,m1.subject,m1.fromname,m1.fromaddr,m1.text,m1.utcdate as localdate "
                           "FROM mailarc m1 "
                           "WHERE "
                           "" + sql_query, args)
            mailarchive_realm = Resource('mailarchive')

            for id,subject,fromname,fromaddr, text,localdate in cursor:
                #if 'WIKI_VIEW' in req.perm('wiki', name):
                resource = mailarchive_realm(id=id,version=None)
                if 'MAILARCHIVE_VIEW' not in req.perm(resource):
                    continue

                yield (req.href.mailarchive(id),
                       subject,
                       datetime.fromtimestamp(localdate, utc),
                       get_author(fromname,fromaddr),
                       shorten_result(text, terms))



class MailArchiveModule(Component):
    implements(ITemplateProvider, ITemplateStreamFilter, IRequestFilter,
        IRequestHandler,IEnvironmentSetupParticipant,INavigationContributor,
        IPermissionRequestor,ILegacyAttachmentPolicyDelegate,IResourceManager)

    FIELD_XPATH = 'div[@id="ticket"]/table[@class="properties"]/td[@headers="h_%s"]/text()'
    
    # IRequestFilter methods
    def pre_process_request(self, req, handler):
        return handler
        
    def post_process_request(self, req, template, data, content_type):
        if req.path_info.startswith('/ticket/'):
            ticket = data['ticket']
            if not isinstance(ticket, Ticket):
                ticket = Ticket(self.env, ticket)
            mail_id_plain = ticket['mail_id']
            mail_ids = []
            if mail_id_plain is None:
                mail_ids = []
            else:
                for mail_id in mail_id_plain.split(','):
                    mail_ids.append(mail_id.lstrip())

            data['mailarchive'] = {
                'mail_id': linkify_ids(self.env, req, mail_ids),
            }
        return template, data, content_type

    # ITemplateStreamFilter methods
    def filter_stream(self, req, method, filename, stream, data):
        if 'mailarchive' in data:
            value =  data['mailarchive']['mail_id']
            stream |= Transformer(self.FIELD_XPATH % 'mail_id').replace(value)
        return stream

    # INavigationContributor methods
    
    def get_active_navigation_item(self, req):
        return 'mailarchive'

    def get_navigation_items(self, req):
        if 'MAILARCHIVE_VIEW' in req.perm('mailarchive'):
            yield ('mainnav', 'mailarchive',
                   tag.a(_('MailArchive'), href=req.href.mailarchive()))

    # ITemplateProvider methods

    def get_htdocs_dirs(self):
        return [('mailarchive',pkg_resources.resource_filename(__name__, 'htdocs'))]

    def get_templates_dirs(self):
        return [pkg_resources.resource_filename(__name__, 'templates')]


    # IRequestHandler methods

    def match_request(self, req):
        match = re.match(r'^/mailarchive(?:/(.*))?', req.path_info)
        if match:
            if match.group(1):
                req.args['messageid'] = match.group(1)
            return 1

    def process_request(self, req):
        req.perm.assert_permission('MAILARCHIVE_VIEW')
        db = self.env.get_db_cnx()

        messageid = req.args.get('messageid', '')

        #id = req.args.get('id','')
        action = req.args.get('action', 'list')
        
        if action == 'import':
            # brefore import lock db , in order to avoid import twice
            self.import_unixmails(req.remote_addr,db)
            # after import unlock db
            return self._render_list(req, db, False,False)
        
        elif messageid != '':
            return self._render_view(req, db, messageid)
        else:
            # did the user ask for any special report?
            return self._render_list(req, db, False,False)

    # IEnvironmentSetupParticipant methods

    ienvironment_log = ""
    def environment_created(self):
        pass

    def environment_needs_upgrade(self, db):
        cursor = db.cursor()
        try:
            cursor.execute("SELECT id FROM mailarc WHERE id='1'")
        except :
            db.rollback()
            self.log.debug('MailArchive environment_needs_upgrade')
            return True
        
        return False
        
    def upgrade_environment(self, db):
        self.log.debug('MailArchive upgrade_environment')

        sql = [
"""
CREATE TABLE mailarc (id integer,category text, messageid text ,
 utcdate integer, zoneoffset integer, 
 subject text, fromname text, fromaddr text, header text, text text, 
 threadroot text, threadparent text);
""",
"""
CREATE TABLE mailarc_category ( category text,mlid text ,yearmonth text ,count integer);
""",
"""
CREATE UNIQUE INDEX mailarc_messageid_idx ON mailarc (messageid)
""",
"""
CREATE INDEX mailarc_id_idx ON mailarc (id)
""",
"""
CREATE INDEX mailarc_category_idx ON mailarc (category)
""",
"""
CREATE INDEX mailarc_utcdate_idx ON mailarc (utcdate)
""",
"""
CREATE UNIQUE INDEX mailarc_category_category_idx ON mailarc_category (category)
""",
        ]

        
        #db = self.env.get_db_cnx()
        cursor = db.cursor()
        for s in sql:
            cursor.execute(s)
            self.log.debug('%s' % s)

        #pass

    def _get_category_href(self,req,category):
        return req.href.mailarchive()+'?category=%s' % category
    def _get_href(self,req,id):
        return req.href.mailarchive(id)

    def _render_view(self, req, db, id):
        title, description, sql = ('ML名','MLの説明','select * from mailarc')
        #req.hdf['mailarc.action'] = 'view'
        data = {}
        data['action'] = 'view'

        target_threadroot = ''
        cursor = db.cursor()
        cursor.execute("SELECT id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,text,threadroot FROM mailarc WHERE id=%s",(id,))

        #messages = []
        for id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,text,threadroot in cursor:
            prefix ='mailarc'
            
            #zone and date
            zone = ''
            if zoneoffset == '':
                zoneoffset = 0
            if zoneoffset >0:
                zone = ' +' + time.strftime('%H%M',time.gmtime(zoneoffset))
            elif zoneoffset < 0:
                zone = ' -' + time.strftime('%H%M',time.gmtime(-1*zoneoffset))

            localdate = time.strftime("%Y/%m/%d %H:%M:%S",time.gmtime(utcdate+zoneoffset))

            # from
            fromtext = ''
            fromaddr = fromaddr.replace('@',
                    self.env.config.get('mailarchive', 'replaceat','@'))
            #if fromname=='':
            #    fromtext = fromaddr
            #else:
            #    fromtext = '%s (%s)' % (fromname,fromaddr)
            fromtext = get_author(fromname,fromaddr)
        
            #subjectが空だとリンクにならない。
            if subject == None or subject.strip()=='':
                subject = '___'
            
            message = {
                'id':id,
                'subject':subject,
                'href':req.href.mailarchive(id),
                'fromname':fromtext,
                'fromaddr':fromaddr,
                'senddate':localdate + zone,
                'messageid':messageid
            }
            target_threadroot = threadroot

            text = text.replace('@',self.env.config.get('mailarchive', 'replaceat','_at_') )

            contentlines = text.splitlines()
            htmllines = ['',]
            
            message['plain_body'] = text

            #customize!
            #http://d.hatena.ne.jp/ohgui/20090604/1244114483
            wikimode = req.args.get('wikimode', 'on')
            for line in contentlines:
                if self.env.config.get('mailarchive', 'wikiview','enabled') == 'enabled' and wikimode == 'on':
                    #htmllines.append(wiki_to_oneliner(line, self.env,db,True,True,req))
                    htmllines.append(wiki_to_oneliner(line, self.env,db,False,False,req))
                else:
                    htmllines.append(Markup(Markup().escape(line).replace(' ','&nbsp;')))
                
            content = Markup('<br/>').join(htmllines)

            message['page_html'] = content
            #messages.append(message)
            
            break

        # Todo:Raise error when messsageid is wrong.
        # List attached files
        #req.perm.require('ATTACHMENT_VIEW')
        context = Context.from_request(req, Resource('mailarchive', str(id), None))
        #self.log.debug(context)
        data['attachments']=AttachmentModule(self.env).attachment_data(context)
        #self.log.debug(data['attachments'])
        #req.hdf['mailarc.attachments'] = attachments_to_hdf(self.env, req, db,
        #                                                   'mailarchive', id)
        

        #if req.perm.has_permission('TICKET_APPEND'):
        #req.hdf['mailarc.attach_href'] = self.env.href.attachment('mailarchive',
        #                                                             id)

        if 'mailarc_mails' in req.session:
            self.log.debug(req.session['mailarc_mails'])
            mails = req.session['mailarc_mails'].split()
            if str(id) in mails:
                idx = mails.index(str(id))
                if idx > 0:
                    #add_ctxtnav(req, _('first'), req.href.mailarchive(mails[0]))
                    add_link(req, _('prev'), req.href.mailarchive(mails[idx - 1]))
                if idx < len(mails) - 1:
                    add_link(req, _('next'), req.href.mailarchive(mails[idx + 1]))
                    #add_ctxtnav(req, _('last'), req.href.mailarchive(mails[-1]))
                add_link(req, _('up'), req.session['mailarc_category_href'])
                prevnext_nav(req,u'メール', u'リストに戻る')


        #if target_threadroot == '':
        #    target_threadroot = messageid
        
        ref_count=0
        reflist = []
        cursor.execute("SELECT id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,text,threadroot FROM mailarc WHERE messageid=%s or threadroot=%s ORDER BY utcdate",(target_threadroot,target_threadroot))
        for ref_id,ref_messageid,utcdate,zoneoffset,subject,fromname,fromaddr,text,threadroot in cursor:
            ref_count = ref_count +1
            #subjectが空だとリンクにならない。
            if subject == None or subject.strip()=='':
                subject = '___'
            
            ref ={
                'id':str(ref_id),
                'subject':subject[:20],
                'subject_alt':subject,
                'fromname':get_author(fromname,fromaddr),
                'date':time.strftime("%Y/%m/%d %H:%M:%S",time.gmtime(utcdate+zoneoffset)),
                'href':''
            }
            if messageid == ref_messageid:
                pass
            else:
                ref['href'] =req.href.mailarchive(ref_id)
            reflist.append(ref)

        add_stylesheet(req, 'mailarchive/css/mailarchive.css')
        data['reflist'] = reflist
        data['message'] = message
        
        data['related_tickets'] = self._collect_related_tickets(req, db, id)
        
        return 'maildetail.html', data, None
    
    def _collect_related_tickets(self, req, db, id):
        cursor = db.cursor()
        #cursor.execute("SELECT t.id, c.value FROM ticket t LEFT OUTER JOIN ticket_custom c ON (t.id = c.ticket AND c.name = 'mail_id' AND c.value = '%s')" % str(message['id']))
        cursor.execute("SELECT ticket, value FROM ticket_custom WHERE name='mail_id'")
        tickets = []
        for ticket_id, mail_id in cursor:
            #print ticket_id, mail_id, id
            if mail_id != None and mail_id.find(str(id)) != -1:
                tickets.append(Ticket(self.env, ticket_id))
                
        return tickets
        
    def month_add(self,year,month,add_month):
        month = month + add_month
        while month >12 or month <1:
            if month > 12:
                month = month - 12
                year = year + 1
            else :
                month = month + 12
                year = year - 1

    # Internal methods
    def _render_list(self, req, db, thread_flag , month):
        target_category = req.args.get('category', '')
        #month = req.args.get('month', '')
        #this_month = time.strftime("%Y/%m/%d %H:%M:%S",time.gmtime(utcdate+zoneoffset))

        data = {}

        ids = ['',]

        title, description, sql = ('ML名','MLの説明','select * from mailarc')
        #req.hdf['mailarc.mode'] = 'list'
        data['mode'] = 'list'
        
        mesid_prefix = {}

        cursor = db.cursor()
        cursor.execute("SELECT category,mlid,yearmonth,count FROM mailarc_category ORDER BY mlid,yearmonth  DESC")

        mls = []
        pre_mlid = ''
        for category,mlid,yearmonth,count in cursor:
            if target_category == '':
                target_category = category 

            category_item = {
                'id': mlid + yearmonth,
                'name': mlid,
                'year': yearmonth[:4],
                'month': yearmonth[4:],
                'count': str(count),
                'href': self._get_category_href(req,category)
            }
            if category == target_category:
                data['name'] = mlid
                data['year'] = yearmonth[:4]
                data['month'] = yearmonth[4:]
                category_item['href'] = ""

            if pre_mlid != mlid:
                mls.append({'name':mlid,'yearmonths':[]})
                pre_mlid = mlid
            mls[-1]['yearmonths'].append(category_item)

        attachments_list = {}
        cursor.execute("SELECT DISTINCT attachment.id as id ,mailarc.id as id2, utcdate FROM mailarc,attachment WHERE mailarc.category=%s AND CAST('mailarc.id' as text) = attachment.id AND attachment.type='mailarchive' ORDER BY utcdate",(target_category.encode('utf-8'),))
        for id,id2,utcdate in cursor:
            attachments_list[str(id)] = 1

        thread_flag = True
        cursor.execute("SELECT id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,threadparent,threadroot FROM mailarc WHERE category=%s ORDER BY utcdate",(target_category.encode('utf-8'),))

        #pagelize
        results = []
        for id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,thread_parent,thread_root in cursor:
            results.append((id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,thread_parent,thread_root))
        pagelized = self._pagelize_list(req,results,data)

        #make message tree
        root_message = {
                'children':[]
        }
        messageid_to_message = {'':root_message}
        for id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,thread_parent,thread_root in pagelized.items:
            #subjectが空だとリンクにならない。
            if subject == None or subject.strip()=='':
                subject = '___'

            #date
            if zoneoffset == '':
                zoneoffset = 0
            localdate = time.strftime("%Y/%m/%d %H:%M:%S",time.gmtime(utcdate+zoneoffset))
            zone = ''
            if zoneoffset >0:
                zone = ' +' + time.strftime('%H%M',time.gmtime(zoneoffset))
            elif zoneoffset < 0:
                zone = ' -' + time.strftime('%H%M',time.gmtime(-1*zoneoffset))

            message = {
                'subject':subject,
                'mail_href':req.href.mailarchive(id),
                'fromname':fromname,
                'senddate':localdate + zone,
                'threadparent':thread_parent,
                'threadroot':thread_root,
                'attachment':0,
                'children':[]
            }
            if fromname=='' and re.match('(.+?)@',fromaddr):
                message['fromname'] = re.match('(.+?)@',fromaddr).group(1)
           
            if attachments_list.has_key(str(id)) :
                message['attachment'] = 1
 
            #Search Parent
            messages = self._serach_parent(messageid_to_message,thread_parent)
            if messages.has_key('children'):
                messages['children'].append(message)
                #ソートを逆順にするにはappendでなくinsertを使うこと
            messageid_to_message[messageid] = message

            ids.append(id)

        idstext = ''.join(['%s ' % id for id in ids])
        self.log.debug("Idtext: %s" % idstext)
        req.session['mailarc_mails'] = idstext
        req.session['mailarc_category_href'] = self._get_category_href(req,target_category)

        data['messages'] = root_message
        data['mls'] = mls;

        add_stylesheet(req, 'mailarchive/css/mailarchive.css')

        return 'mailarchive.html', data, None


    def _pagelize_list(self,req,results,data):
        # get page from req(default page = max_page)
        page = int(req.args.get('page', '-1'))
        num_item_per_page = int(self.env.config.get('mailarchive', 'items_page','50'))
        num_shown_pages = int(self.env.config.get('mailarchive', 'shown_pages','30'))
        if page == -1:
            results_temp = Paginator(results, 0, num_item_per_page)
            page = results_temp.num_pages 

        results = Paginator(results, page - 1, num_item_per_page)
        
        pagedata = []    
        data['page_results'] = results
        shown_pages = results.get_shown_pages(num_shown_pages)
        for shown_page in shown_pages:
            page_href = req.href.mailarchive(category=req.args.get('category',None),
                                        page=shown_page, noquickjump=1)
            pagedata.append([page_href, None, str(shown_page),
                             'page ' + str(shown_page)])

        fields = ['href', 'class', 'string', 'title']
        results.shown_pages = [dict(zip(fields, p)) for p in pagedata]
        
        results.current_page = {'href': None, 'class': 'current',
                                'string': str(results.page + 1),
                                'title':None}

        if results.has_next_page:
            next_href = req.href.mailarchive(category=req.args.get('category',None),
                                        page=page + 1)
            add_link(req, 'next', next_href, _('Next Page'))

        if results.has_previous_page:
            prev_href = req.href.mailarchive(category=req.args.get('category',None),
                                        page=page - 1)
            add_link(req, 'prev', prev_href, _('Previous Page'))

        data['page_href'] = req.href.mailarchive(category=req.args.get('category',None))
        return results

    def _serach_parent(self,messageid_to_message,thread_parent):
        parents = thread_parent.split(' ')
        for parent in parents:
            if messageid_to_message.has_key(parent) == True:
                self.log.debug('Thread:%s' % parent)
                return messageid_to_message[parent]
        return messageid_to_message['']



    # IPermissionRequestor method
    
    def get_permission_actions(self):
        return ['MAILARCHIVE_VIEW',
                ('MAILARCHIVE_ADMIN', ['MAILARCHIVE_VIEW']),
                ]

    # ILegacyAttachmentPolicyDelegate methods
    
    def check_attachment_permission(self, action, username, resource, perm):
        """ Respond to the various actions into the legacy attachment
        permissions used by the Attachment module. """
        if resource.parent.realm == 'mailarchive':
            if action == 'ATTACHMENT_VIEW':
                return 'MAILARCHIVE_VIEW' in perm(resource.parent)
            if action in ['ATTACHMENT_CREATE', 'ATTACHMENT_DELETE']:
                if 'MAILARCHIVE_ADMIN' in perm(resource.parent):
                    return True
                else:
                    return True # False

    # IResourceManager methods
    
    def get_resource_realms(self):
        yield 'mailarchive'

    def get_resource_url(self, resource, href, **kwargs):
        return href.mailarchive(resource.id)
        
    def get_resource_description(self, resource, format=None, context=None,
                                 **kwargs):
        if context:
            return tag.a('mail:'+resource.id, href=context.href.mailarchive(resource.id))
        else:
            return 'mail:'+resource.id
        


